From: Joost Verdoorn Date: Thu, 21 Jun 2012 22:52:56 +0000 (+0200) Subject: viewSelector: Merge SearchTab into the ViewSelector X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~6110 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=c267a7a7f9c7c340e60c63aad1bf42923f19f292;p=gnome-shell.git viewSelector: Merge SearchTab into the ViewSelector Tabs used to provide an abstraction for a page and the control used to activate it. As the latter has now been replaced with external controls handled directly in the viewSelector, the abstraction itself doesn't make much sense anymore. In preparation of replacing it, move the search handling provided by the SearchTab directly in the viewSelector. https://bugzilla.gnome.org/show_bug.cgi?id=682109 --- diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 38326a59b..0d382a21e 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -98,11 +98,33 @@ const ViewTab = new Lang.Class({ }); -const SearchTab = new Lang.Class({ - Name: 'SearchTab', - Extends: BaseTab, +const ViewSelector = new Lang.Class({ + Name: 'ViewSelector', + + _init : function(searchEntry, showAppsButton) { + this.actor = new St.BoxLayout({ name: 'viewSelector', + vertical: true }); + + this._showAppsButton = showAppsButton; + this._showAppsButton.connect('notify::checked', Lang.bind(this, + function() { + if (this._showAppsButton.checked) + this._switchTab(this._appsTab); + else + this._switchTab(this._windowsTab); + })); + + // The page area holds the tab pages. Every page is given the + // area's full allocation, so that the pages would appear on top + // of each other if the inactive ones weren't hidden. + this._pageArea = new Shell.Stack(); + this.actor.add(this._pageArea, { x_fill: true, + y_fill: true, + expand: true }); + + this._tabs = []; + this._activeTab = null; - _init: function(searchEntry) { this.active = false; this._searchPending = false; this._searchTimeoutId = 0; @@ -111,8 +133,18 @@ const SearchTab = new Lang.Class({ this._entry = searchEntry; ShellEntry.addContextMenu(this._entry); + this._text = this._entry.clutter_text; + this._text.connect('text-changed', Lang.bind(this, this._onTextChanged)); this._text.connect('key-press-event', Lang.bind(this, this._onKeyPress)); + this._text.connect('key-focus-in', Lang.bind(this, function() { + this._searchResults.highlightDefault(true); + })); + this._text.connect('key-focus-out', Lang.bind(this, function() { + this._searchResults.highlightDefault(false); + })); + this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped)); + global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged)); this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon', icon_name: 'edit-find', @@ -123,38 +155,29 @@ const SearchTab = new Lang.Class({ this._entry.set_secondary_icon(this._inactiveIcon); this._iconClickedId = 0; + this._capturedEventId = 0; - this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); - this.parent(new St.Bin() /* Dummy */, this._searchResults.actor, _("Search"), 'edit-find'); - - this._text.connect('text-changed', Lang.bind(this, this._onTextChanged)); - this._text.connect('key-press-event', Lang.bind(this, function (o, e) { - // We can't connect to 'activate' here because search providers - // might want to do something with the modifiers in activateDefault. - let symbol = e.get_key_symbol(); - if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._doSearch(); - } - this._searchResults.activateDefault(); - return true; - } - return false; - })); + this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); + this._windowsTab = new ViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic'); + this._addViewTab(this._windowsTab); - this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped)); + let appView = new AppDisplay.AllAppDisplay(); + this._appsTab = new ViewTab('applications', _("Applications"), appView.actor, 'system-run'); + this._addViewTab(this._appsTab); - global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged)); + this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); + this._searchTab = new BaseTab(new St.Bin(), this._searchResults.actor, _("Search"), 'edit-find'); + this._addTab(this._searchTab); - this._capturedEventId = 0; + // Default search providers + // Wanda comes obviously first + this.addSearchProvider(new Wanda.WandaSearchProvider()); + this.addSearchProvider(new AppDisplay.AppSearchProvider()); + this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); + this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); - this._text.connect('key-focus-in', Lang.bind(this, function() { - this._searchResults.highlightDefault(true); - })); - this._text.connect('key-focus-out', Lang.bind(this, function() { - this._searchResults.highlightDefault(false); - })); + // Load remote search providers provided by applications + RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); // Since the entry isn't inside the results container we install this // dummy widget as the last results container child so that we can @@ -172,10 +195,130 @@ const SearchTab = new Lang.Class({ this._searchResults.actor.add_actor(this._focusTrap); global.focus_manager.add_group(this._searchResults.actor); + + Main.overview.connect('item-drag-begin', + Lang.bind(this, this._resetShowAppsButton)); + + this._stageKeyPressId = 0; + Main.overview.connect('showing', Lang.bind(this, + function () { + this._resetShowAppsButton(); + this._stageKeyPressId = global.stage.connect('key-press-event', + Lang.bind(this, this._onStageKeyPress)); + })); + Main.overview.connect('hiding', Lang.bind(this, + function () { + this._resetShowAppsButton(); + if (this._stageKeyPressId != 0) { + global.stage.disconnect(this._stageKeyPressId); + this._stageKeyPressId = 0; + } + })); + + // Public constraints which may be used to tie actors' height or + // vertical position to the current tab's content; as the content's + // height and position depend on the view selector's style properties + // (e.g. font size, padding, spacing, ...) it would be extremely hard + // and ugly to get these from the outside. While it would be possible + // to use position and height properties directly, outside code would + // need to ensure that the content is properly allocated before + // accessing the properties. + this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, + coordinate: Clutter.BindCoordinate.HEIGHT }); + }, + + show: function() { + this._workspacesDisplay.show(); + + if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) + Main.overview.fadeOutDesktop(); + }, + + zoomFromOverview: function() { + this._workspacesDisplay.zoomFromOverview(); + + if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) + Main.overview.fadeInDesktop(); }, hide: function() { - this.parent(); + this._workspacesDisplay.hide(); + }, + + _addTab: function(tab) { + tab.page.hide(); + this._pageArea.add_actor(tab.page); + tab.connect('activated', Lang.bind(this, function(tab) { + this._switchTab(tab); + })); + }, + + _addViewTab: function(viewTab) { + this._tabs.push(viewTab); + this._addTab(viewTab); + }, + + _switchTab: function(tab) { + let firstSwitch = this._activeTab == null; + + if (this._activeTab && this._activeTab.visible) { + if (this._activeTab == tab) + return; + this._activeTab.hide(); + } + + if (tab != this._searchTab) { + this._activeTab = tab; + if (this._searchTab.visible) { + this._searchTab.hide(); + } + } + + // Only fade when switching between tabs, + // not when setting the initially selected one. + if (!tab.visible) + tab.show(!firstSwitch); + }, + + switchTab: function(id) { + for (let i = 0; i < this._tabs.length; i++) + if (this._tabs[i].id == id) { + this._switchTab(this._tabs[i]); + break; + } + }, + + _resetShowAppsButton: function() { + this._showAppsButton.checked = false; + }, + + _onStageKeyPress: function(actor, event) { + let modifiers = event.get_state(); + let symbol = event.get_key_symbol(); + + if (symbol == Clutter.Escape) { + if (this.active) + this.reset(); + else + Main.overview.hide(); + return true; + } else if (Clutter.keysym_to_unicode(symbol) || + (symbol == Clutter.BackSpace && this.active)) { + this.startSearch(event); + } else if (!this.active) { + if (symbol == Clutter.Tab) { + this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); + return true; + } else if (symbol == Clutter.ISO_Left_Tab) { + this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false); + return true; + } + } + return false; + }, + + _searchCancelled: function() { + this._switchTab(this._activeTab); // Leave the entry focused when it doesn't have any text; // when replacing a selected search term, Clutter emits @@ -224,16 +367,6 @@ const SearchTab = new Lang.Class({ } }, - addSearchProvider: function(provider) { - this._searchSystem.registerProvider(provider); - this._searchResults.createProviderMeta(provider); - }, - - removeSearchProvider: function(provider) { - this._searchSystem.unregisterProvider(provider); - this._searchResults.destroyProviderMeta(provider); - }, - startSearch: function(event) { global.stage.set_key_focus(this._text); this._text.event(event, false); @@ -260,14 +393,14 @@ const SearchTab = new Lang.Class({ this.reset(); })); } - this._activate(); + this._switchTab(this._searchTab); } else { if (this._iconClickedId > 0) this._entry.disconnect(this._iconClickedId); this._iconClickedId = 0; this._entry.set_secondary_icon(this._inactiveIcon); - this.emit('search-cancelled'); + this._searchCancelled(); } if (!this.active) { if (this._searchTimeoutId > 0) { @@ -288,6 +421,15 @@ const SearchTab = new Lang.Class({ this.reset(); return true; } + } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { + // We can't connect to 'activate' here because search providers + // might want to do something with the modifiers in activateDefault. + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._doSearch(); + } + this._searchResults.activateDefault(); + return true; } else if (this.active) { let arrowNext, nextDirection; if (entry.get_text_direction() == Clutter.TextDirection.RTL) { @@ -337,191 +479,17 @@ const SearchTab = new Lang.Class({ let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); this._searchResults.doSearch(text); - return false; - } -}); - - -const ViewSelector = new Lang.Class({ - Name: 'ViewSelector', - - _init : function(searchEntry, showAppsButton) { - this.actor = new St.BoxLayout({ name: 'viewSelector', - vertical: true }); - - this._showAppsButton = showAppsButton; - this._showAppsButton.connect('notify::checked', Lang.bind(this, - function() { - if (this._showAppsButton.checked) - this._switchTab(this._appsTab); - else - this._switchTab(this._windowsTab); - })); - - // The page area holds the tab pages. Every page is given the - // area's full allocation, so that the pages would appear on top - // of each other if the inactive ones weren't hidden. - this._pageArea = new Shell.Stack(); - this.actor.add(this._pageArea, { x_fill: true, - y_fill: true, - expand: true }); - - this._tabs = []; - this._activeTab = null; - - this._searchTab = new SearchTab(searchEntry); - this._addTab(this._searchTab); - - this._searchTab.connect('search-cancelled', Lang.bind(this, - function() { - this._switchTab(this._activeTab); - })); - - this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); - this._windowsTab = new ViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic'); - this._addViewTab(this._windowsTab); - - let appView = new AppDisplay.AllAppDisplay(); - this._appsTab = new ViewTab('applications', _("Applications"), appView.actor, 'system-run'); - this._addViewTab(this._appsTab); - - // Default search providers - // Wanda comes obviously first - this.addSearchProvider(new Wanda.WandaSearchProvider()); - this.addSearchProvider(new AppDisplay.AppSearchProvider()); - this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); - this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); - - // Load remote search providers provided by applications - RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); - - Main.overview.connect('item-drag-begin', - Lang.bind(this, this._resetShowAppsButton)); - - this._stageKeyPressId = 0; - Main.overview.connect('showing', Lang.bind(this, - function () { - this._resetShowAppsButton(); - this._stageKeyPressId = global.stage.connect('key-press-event', - Lang.bind(this, this._onStageKeyPress)); - })); - Main.overview.connect('hiding', Lang.bind(this, - function () { - this._resetShowAppsButton(); - if (this._stageKeyPressId != 0) { - global.stage.disconnect(this._stageKeyPressId); - this._stageKeyPressId = 0; - } - })); - - // Public constraints which may be used to tie actors' height or - // vertical position to the current tab's content; as the content's - // height and position depend on the view selector's style properties - // (e.g. font size, padding, spacing, ...) it would be extremely hard - // and ugly to get these from the outside. While it would be possible - // to use position and height properties directly, outside code would - // need to ensure that the content is properly allocated before - // accessing the properties. - this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, - coordinate: Clutter.BindCoordinate.HEIGHT }); - }, - - show: function() { - this._workspacesDisplay.show(); - - if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) - Main.overview.fadeOutDesktop(); - }, - - zoomFromOverview: function() { - this._workspacesDisplay.zoomFromOverview(); - - if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) - Main.overview.fadeInDesktop(); - }, - - hide: function() { - this._workspacesDisplay.hide(); - }, - - _addTab: function(tab) { - tab.page.hide(); - this._pageArea.add_actor(tab.page); - tab.connect('activated', Lang.bind(this, function(tab) { - this._switchTab(tab); - })); - }, - - _addViewTab: function(viewTab) { - this._tabs.push(viewTab); - this._addTab(viewTab); - }, - - _switchTab: function(tab) { - let firstSwitch = this._activeTab == null; - - if (this._activeTab && this._activeTab.visible) { - if (this._activeTab == tab) - return; - this._activeTab.hide(); - } - - if (tab != this._searchTab) { - this._activeTab = tab; - if (this._searchTab.visible) { - this._searchTab.hide(); - } - } - - // Only fade when switching between tabs, - // not when setting the initially selected one. - if (!tab.visible) - tab.show(!firstSwitch); - }, - - switchTab: function(id) { - for (let i = 0; i < this._tabs.length; i++) - if (this._tabs[i].id == id) { - this._switchTab(this._tabs[i]); - break; - } - }, - - _resetShowAppsButton: function() { - this._showAppsButton.checked = false; - }, - - _onStageKeyPress: function(actor, event) { - let modifiers = event.get_state(); - let symbol = event.get_key_symbol(); - - if (symbol == Clutter.Escape) { - if (this._searchTab.active) - this._searchTab.reset(); - else - Main.overview.hide(); - return true; - } else if (Clutter.keysym_to_unicode(symbol) || - (symbol == Clutter.BackSpace && this._searchTab.active)) { - this._searchTab.startSearch(event); - } else if (!this._searchTab.active) { - if (symbol == Clutter.Tab) { - this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); - return true; - } else if (symbol == Clutter.ISO_Left_Tab) { - this._activeTab.page.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false); - return true; - } - } return false; }, addSearchProvider: function(provider) { - this._searchTab.addSearchProvider(provider); + this._searchSystem.registerProvider(provider); + this._searchResults.createProviderMeta(provider); }, removeSearchProvider: function(provider) { - this._searchTab.removeSearchProvider(provider); + this._searchSystem.unregisterProvider(provider); + this._searchResults.destroyProviderMeta(provider); } }); Signals.addSignalMethods(ViewSelector.prototype);